#pragma once
#include "Connection.hpp"
#include "Epoller.hpp"
#include"Util.hpp"
#include <unordered_map>
#include <sys/epoll.h>

// Reactor:基于事件来处理所有IO任务 --> 将TcpServer 当做一个存放所有连接的容器；epoll_wait 知道哪些事件就绪了就会告诉上层
// 对应处于激活状态的文件会去调用自己的IO方法，那么TcpServer 叫做Reactor 更为合适
//  class TcpServer
// 按照往常经验，会在Tcpserver 中设置监听套接字作为成员属性，这样的话，每创建一个TcpServer 对象就会有一个监听套接字
//  实际上对于服务器来说只需要有一个监听套接字，所以此处最好将监听套接字单独实现-->继承Connection --> Listener
class Reactor
{
private:
    static const int gsize = 128;
    bool IsExist(const std::shared_ptr<Connection> &conn)
    {
        auto pos = _connections.find(conn->Sockfd());
        return pos != _connections.end();
    }
    bool IsExist(int sockfd)
    {
        auto pos = _connections.find(sockfd);
        return pos != _connections.end();
    }

public:
    Reactor() : _epoller(std::make_unique<Epoller>())
    {
    }
    ~Reactor() {}
    void AddConnection(const std::shared_ptr<Connection> &conn)
    {
        // 判断_connections 中是否存在这个文件描述符
        if (IsExist(conn))
        {
            LOG(LogLevel::INFO) << conn->Sockfd() << "已经在 _connections 中存在";
            return;
        }

        // 设置回指指针
        conn->SetOwn(this);

        // 添加进_connections、设置进epoll 内核
        _connections.insert(std::make_pair(conn->Sockfd(), conn));
        // 设置进epoll 内核
        _epoller->AddEvent(conn->Sockfd(), conn->Events()); // 关心什么文件描述符、关心的事件有哪些
    }
    void LoopOnce(int timeout)
    {
        int n = _epoller->wait(_revs, gsize, timeout);
        for (int i = 0; i < n; i++) // 遍历_connections数组，判断其关心的事件然后派发
        {
            int sockfd = _revs[i].data.fd;
            uint32_t events = _revs[i].events;

            // 处理epoll_wait 可能存在的异常，交到IO中处理；统一异常处理
            if ((events & EPOLLERR))
            {
                events = (EPOLLIN | EPOLLOUT);
            }
            if ((events & EPOLLHUP))
            {
                events = (EPOLLIN | EPOLLOUT);
            }

            if ((events & EPOLLIN) && IsExist(sockfd)) // 读事件就绪并且该文件描述符在 _connections 中存在
            {
                _connections[sockfd]->Recver();
            }

            if ((events & EPOLLOUT) && IsExist(sockfd))
            {
                _connections[sockfd]->Sender();
            }
        }
    }

    // 实现运行的功能
    void Dispatcher() //--> 派发功能
    {
        int timeout = 1000;
        // 需要让Epoller来等待
        while (true)
        {
            LoopOnce(timeout);
        }
    }

    void EnableReadWrite(int sockfd, bool enableread, bool enablewrite)
    {
        // 代码的鲁棒性，先判断 sockfd 中是否存在于 _connections 中
        if (!IsExist(sockfd))
        {
            LOG(LogLevel::WARNING) << sockfd << " conn not in Reactor , bug";
            return;
        }

        // 设置events 事件
        uint32_t events = (enableread ? EPOLLIN : 0) | (enablewrite ? EPOLLOUT : 0) | EPOLLET; // ET模式
        // 设置进_connections 中
        _connections[sockfd]->SetEvents(events);

        // 设置进epoll内核当中
        _epoller->ModEvent(_connections[sockfd]->Sockfd(), _connections[sockfd]->Events());
    }
    void DelConection(int sockfd)
    {
        // 代码的鲁棒性，判断sockfd 在哈希表中是否存在
        if (!IsExist(sockfd))
        {
            LOG(LogLevel::WARNING) << sockfd << " conn not in Reactor , bug";
            return;
        }
        // 将这个sockfd 从epoll 中移除
        _epoller->DelEvent(sockfd);
        // 从_connections 中移除
        _connections.erase(sockfd);
        // 关闭文件描述符
        close(sockfd);
    }

    //为当前测试形成一个报表，实际上就是遍历哈希表
    void ShowConnection()
    {
        std::cout << "########################" << std::endl;
        for(auto& conn: _connections)
        {
            std::cout << conn.second->Sockfd() << " : " << Events2String(conn.second->Events()) << std::endl;

        }
        std::cout << "########################" << std::endl;
    }


    //管理链接 ---> 在Connection 中增加一个成员属性来记录连接最新的活跃时间
    void CheckTimeOut()
    {
        //获取当前时间
        uint64_t currtime = time(nullptr);
        //遍历哈希表，获取连接的最近活跃时间，判断是否超时
        for(auto& conn: _connections)
        {
            uint64_t t = currtime - conn.second->LastTime();
            if(t > 5*60*1000)
            {
                //将这个链接从哈希表中移除
                DelConection(conn.second->Sockfd());
            }
        }

    }

private:
    // 在TcpServer 中会存在大量的连接，需要管理起来，使用unordered_map , 让fd 作为键值;让连接基类Connection 指针作为value
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;
    std::unique_ptr<Epoller> _epoller; // 多路转接指针

    struct epoll_event _revs[gsize];
};